﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using snat.model;
using snat.algorithms.network.TFIDF;

namespace snat.algorithms.node.TFIDF {
    /// <summary>
    /// Looks for information 'spreading' through the network
    /// Specifically tries to find chains of messages, and outputs the longest paths.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class InformationSpreadPredictor<T> :IAlgorithm where T : Document{

        /*

            NetworkModel.Instance.InitNodes(new RelabelFactory("../../../snat/Resources/emails"));

            List<int> peopleToTrain = new List<int>();
            peopleToTrain.Add(107);
            peopleToTrain.Add(144);

            NetworkModel.Instance.Init();
	
			NetworkModel.Instance.Step(new DateTime(2001, 10, 15, 17, 05, 00));

            foreach (PersonalizedFWDTFIDFNode<EnronDocument> n in NetworkModel.Instance.Nodes.Values) {
                if (peopleToTrain.Contains(n.ID)) {
                    NetworkModel.Instance.MessageProcessed += n.NetworkMessageEveryone;
                    n.MessageSend += n.OnMessageSend;
                    n.MessageRecieve += n.OnMessageReceive;
                }
            }

            // train until 31/10/2001 17:04:45

            NetworkModel.Instance.Step(new DateTime(2001, 10, 31, 17, 05, 00));
            InformationSpreadPredictor<EnronDocument> isp = new InformationSpreadPredictor<EnronDocument>();
            isp.Register();

            foreach (PersonalizedFWDTFIDFNode<EnronDocument> n in NetworkModel.Instance.Nodes.Values) {
                if (peopleToTrain.Contains(n.ID)) {
                    NetworkModel.Instance.MessageProcessed -= n.NetworkMessageEveryone;
                    n.MessageSend -= n.OnMessageSend;
                    n.MessageRecieve -= n.OnMessageReceive;
                }
            }
            // step until 31/10/2001 17:28:10

            NetworkModel.Instance.Step(new DateTime(2001, 10, 31, 17, 29, 00));

            // step until 31/10/2001 22:23:34

            NetworkModel.Instance.Step(new DateTime(2001, 10, 31, 22, 24, 00)); 
        


        */
        public List<int> peopleToTrain;
        
        public Dictionary<PersonalizedFWDTFIDFNode<T>, List<DocumentPathRecorder>> dict { get; private set; }

        public Dictionary<Document, List<DocumentPathRecorder>> docPaths;

        public InformationSpreadPredictor() {
            peopleToTrain = new List<int>();
            peopleToTrain.Add(107);
            peopleToTrain.Add(144);

            dict = new Dictionary<PersonalizedFWDTFIDFNode<T>,List<DocumentPathRecorder>>();
            docPaths = new Dictionary<Document, List<DocumentPathRecorder>>();
            foreach (PersonalizedFWDTFIDFNode<T> n in NetworkModel.Instance.Nodes.Values) {
                if (!peopleToTrain.Contains(n.ID)) {
                    continue;
                }
                dict.Add(n, new List<DocumentPathRecorder>());
                // Create a document recorder for all documents that may travel around system from now on
                foreach (Document d in n.ReceivedDocuments) {
                    if (!docPaths.ContainsKey(d)) {
                        docPaths.Add(d, new List<DocumentPathRecorder>());
                    }
                }
            }
        }

        public void OnMessageSend(Message m) {
            if (!peopleToTrain.Contains(m.Sender.ID)) {
                return;
            }
            Console.WriteLine(NetworkModel.Instance.MessageProcessedCount);
            List<PersonalizedFWDTFIDFNode<T>> Recipients =
                m.Recipients.ConvertAll<PersonalizedFWDTFIDFNode<T>>(
                e => NetworkModel.Instance.Nodes[e.ID] as PersonalizedFWDTFIDFNode<T>);
            foreach (PersonalizedFWDTFIDFNode<T> node in Recipients) {
                DocumentPathRecorder dpr = (NetworkModel.Instance.Nodes[m.Sender.ID] as PersonalizedFWDTFIDFNode<T>).IsMessageForwarded(node, round);
                if (dpr != null) {
                    // if it is still in the current list of paths (it hasn't been sent on yet)
                    // then remove it (otherwise it has already been removed as it has already been forwarded).
                    //docPaths[dpr.getDoc()].Remove(dpr);

                    // note down how "far" new document has travelled.
                    int i = dpr.PathLength + 1;

                    // create a new document recorder to note down next stage of journey - these could exist
                    // multiple times
                    DocumentPathRecorder dpr2 = new DocumentPathRecorder(dpr.getDoc());
                    for (int j = 0; j < i; j++) {
                        dpr2.incrPathLength();
                    }
                    docPaths[dpr2.getDoc()].Add(dpr2);
                    dict[node].Add(dpr2);
                }
            }
        }
        int round = 0;
        public void End() {
            foreach (PersonalizedFWDTFIDFNode<T> node in NetworkModel.Instance.Nodes.Values) {
                if (!peopleToTrain.Contains(node.ID)) {
                    continue;
                }

                foreach (DocumentPathRecorder dpr in dict[node]) {
                    Document d = dpr.getDoc();
                    Console.WriteLine("{0}: {1} : AND I HAVE TRAVELLED {2} STEPS ", node.Name, d.ToString(), dpr.PathLength);
                }
                node.ResetMe(dict[node]);
                dict[node] = new List<DocumentPathRecorder>();
            }
            round++;
            Console.WriteLine("ALL NODES RESET READY FOR NEXT CYCLE");
        }

        public string Name {
            get { throw new NotImplementedException(); }
        }

        public void Register() {
            NetworkModel.Instance.MessageProcessed += OnMessageSend;
            NetworkModel.Instance.TapePaused += End;
        }

        public void DeRegister() {
            NetworkModel.Instance.MessageProcessed -= OnMessageSend;
            NetworkModel.Instance.TapePaused -= End;
        }
    }
}
